#include <Render/Include/RendererDX11.h>

#include <Render/Include/RenderObjectDX11.h>

#include <D3DX11async.h>

#include <math.h>

using namespace Render;

// ========== LIFECYCLE ========== //

RendererDX11::RendererDX11(IWinWindow* window)
    :Renderer()
{
    HRESULT hr = S_OK;

    UINT createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_DRIVER_TYPE driverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_REFERENCE,
    };
    UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0] );

    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory( &sd, sizeof( sd ) );
    sd.BufferCount = 1;
    sd.BufferDesc.Width = window->getWidth();
    sd.BufferDesc.Height = window->getHeight();
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = window->getHWND();
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;
    //sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
    //sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

    for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
    {
        D3D_FEATURE_LEVEL featureLevelsRequested = D3D_FEATURE_LEVEL_11_0; //D3D_FEATURE_LEVEL_10_1;
        UINT numFeatureLevelsRequested = 1;
        D3D_FEATURE_LEVEL featureLevelsSupported;
        hr = D3D11CreateDeviceAndSwapChain(NULL, driverTypes[driverTypeIndex], NULL, createDeviceFlags, &featureLevelsRequested, numFeatureLevelsRequested,
                                D3D11_SDK_VERSION, &sd, &(this->swap_chain), &(this->device), &featureLevelsSupported, &(this->immediate_context));
        if (SUCCEEDED(hr))
        {
            break;
        }        
    }

    if( FAILED( hr ) )
        throw 1; //return hr;

    // Create a render target view
    ID3D11Texture2D* pBuffer;
    hr = this->swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*) &pBuffer);
    if (FAILED(hr))
    {
        // swap_chain->Release();
        throw 1;
    }

    this->render_target_view = NULL;
    hr = this->device->CreateRenderTargetView(pBuffer, NULL, &this->render_target_view);
    pBuffer->Release();
    if (FAILED(hr))
    {
        // swap_chain->Release();
        throw 1;
    }

    this->depth_stencil_view = NULL;
    // Create a depth stencil
    ID3D11Texture2D* depth_stencil_buffer;
    D3D11_TEXTURE2D_DESC depthStencilDesc;
    ZeroMemory( &depthStencilDesc, sizeof(depthStencilDesc) );
    depthStencilDesc.Width = window->getWidth();
    depthStencilDesc.Height = window->getHeight();
    depthStencilDesc.MipLevels = 1;
    depthStencilDesc.ArraySize = 1;
    depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    depthStencilDesc.SampleDesc.Count = 1;
    depthStencilDesc.SampleDesc.Quality = 0;
    depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
    depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    depthStencilDesc.CPUAccessFlags = 0;
    depthStencilDesc.MiscFlags = 0;

    hr = this->device->CreateTexture2D(&depthStencilDesc, NULL, &depth_stencil_buffer);
    if (FAILED(hr))
    {
        // render_target_view->Release();
        // swap_chain->Release();
        throw -1;
    }
    
    // Create the depth stencil view
    D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
    ZeroMemory( &descDSV, sizeof(descDSV) );
    descDSV.Format = depthStencilDesc.Format;
    descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
    descDSV.Texture2D.MipSlice = 0;
    hr = this->device->CreateDepthStencilView(depth_stencil_buffer, &descDSV, &this->depth_stencil_view);
    depth_stencil_buffer->Release();
    if (FAILED(hr))
    {
        // depth_stencil_view->Release();
        // render_target_view->Release();
        // swap_chain->Release();
        throw -1;
    }
    
    /// initializes de viewport
    this->viewport.Width = (float)window->getWidth();
    this->viewport.Height = (float)window->getHeight();
    this->viewport.MinDepth = 0.0f;
    this->viewport.MaxDepth = 1.0f;
    this->viewport.TopLeftX = 0.0f;
    this->viewport.TopLeftY = 0.0f;

    /// configures immediate context
    this->immediate_context->OMSetRenderTargets(1, &this->render_target_view, this->depth_stencil_view);
    this->immediate_context->RSSetViewports(1, &this->viewport);

    // Compile the shaders
    
    ID3DBlob* pVSBlob = NULL;
    bool compile_success = this->compileShaderFromFile( L"shaders.hlsl", "VS", "vs_4_0", &pVSBlob );
    if( !compile_success )
    {
        MessageBox( NULL,
            L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
        throw -1;
    }

    // Create the vertex shader
    hr = this->device->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &this->vertex_shader );
    if( FAILED( hr ) )
    {    
        pVSBlob->Release();
        throw -1;
    }

    // Define the input layout
    D3D11_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }        
    };
    UINT numElements = ARRAYSIZE( layout );

    // Create the input layout
    hr = this->device->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
        pVSBlob->GetBufferSize(), &this->input_layout );
    pVSBlob->Release();
    if( FAILED( hr ) )
    {
        throw -1;
    }

    // Set the input layout
    this->immediate_context->IASetInputLayout( this->input_layout );

    // Compile the pixel shader
    ID3DBlob* pPSBlob = NULL;
    compile_success = this->compileShaderFromFile( L"shaders.hlsl", "PS", "ps_4_0", &pPSBlob );
    if( !compile_success )
    {
        MessageBox( NULL,
            L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
        throw -1;
    }

    // Create the pixel shader
    hr = this->device->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &this->pixel_shader );
    pPSBlob->Release();
    if( FAILED( hr ) )
    {
        throw -1;
    }

    // Create the constant buffer
    D3D11_BUFFER_DESC bd;
    ZeroMemory( &bd, sizeof(bd) );
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(ConstantBufferData);
    bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    bd.CPUAccessFlags = 0;
    hr = this->device->CreateBuffer( &bd, NULL, &this->constant_buffer );
    if( FAILED( hr ) )
    {
        throw -1;
    }

    /// creates the default objects used for rendering
    this->createSphereRenderObject();
    this->createCubeRenderObject();
}

RendererDX11::~RendererDX11(void)
{
    delete this->sphere_render_object;
    delete this->cube_render_object;
    this->device->Release();
    this->immediate_context->Release();
    this->swap_chain->Release();
    this->render_target_view->Release();
    this->depth_stencil_view->Release();
    this->vertex_shader->Release();
    this->pixel_shader->Release();
    this->input_layout->Release();
    this->constant_buffer->Release();
}

// ========== OVERLODED OPERATORS ========== //

// ========== METHODS ========== //

void RendererDX11::renderScene(Model::Scene* scene)
{
    float clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; // red, green, blue, alpha

    /// configures immediate context
    this->immediate_context->OMSetRenderTargets(1, &this->render_target_view, this->depth_stencil_view);
    
    this->immediate_context->ClearRenderTargetView(this->render_target_view, clear_color);
    this->immediate_context->ClearDepthStencilView(this->depth_stencil_view, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
  
    this->configViewPoint(scene);

    this->renderSceneWalls(scene);

    const Model::Snake* snake = scene->getSnake();

    const Model::Element* head_element = snake->getHeadElement();
    this->renderSnakeHead(head_element);

    const Model::Element* body_element = head_element->getNextElement();
    while (NULL != body_element)
    {
        this->renderSnakeBodyElement(body_element);
        body_element = body_element->getNextElement();
    }

    const Model::Element* free_element = scene->getFreeElement();
    this->renderSnakeBodyElement(free_element);

    this->drawText();
    this->swap_chain->Present( 0, 0);
    this->clearText();    
}

void RendererDX11::renderBlack()
{
    float clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; // red, green, blue, alpha
    
    this->immediate_context->ClearRenderTargetView(this->render_target_view, clear_color);
    this->immediate_context->ClearDepthStencilView(this->depth_stencil_view, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
    
    this->drawText();

    this->swap_chain->Present( 0, 0);
    
    this->clearText();
}

// ========== GETTERS ========== //

// ========== SETTERS ========== //

// ========== INQUIRY ========== //

// ========== INTERNAL METHODS ========== //

void RendererDX11::createSphereRenderObject()
{
    unsigned int rings = 20;
    unsigned int segments = 20;
    // it is an sphere of radio 1 ---> float radio = 1.0f;

    // I add the two poles
    unsigned int vertex_count = rings * segments + 2;
    VertexData* vertices = new VertexData[vertex_count];

    // south pole
    vertices[0].position = D3DXVECTOR3(0.0f, 0.0f, -1.0f);
    // north pole
    vertices[vertex_count - 1].position = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
    
    float M_PI = 3.14159f;

    float delta_phi = M_PI / (float)( rings + 1 );
    float delta_theta = 2.0f * M_PI / (float)segments;
    
    unsigned int vertex_pos = 1;
    float phi = - M_PI;
    for (unsigned int i = 0; i < rings; i++ )
    {
        phi += delta_phi;
        float theta = 0.0f;
        for (unsigned int j = 0; j < segments; j++ )
        {
            vertices[vertex_pos].position = D3DXVECTOR3( cos(theta) * cos (phi), sin(theta) * cos(phi), sin(phi));
            theta += delta_theta;
            vertex_pos++;
        }
    }

    unsigned int triangles_count = segments * 2 + segments * (rings - 1) * 2;
    unsigned int index_count  = triangles_count * 3;
    unsigned int* indices = new unsigned int[index_count];
    unsigned int index_pos = 0;

    /// south pole triangles
    for (unsigned int i = 0; i < segments; i++)
    {
        indices[index_pos++] = 1 + i;
        indices[index_pos++] = 0; // south pole
        indices[index_pos++] = 1 + ((i + 1) % segments);
    }

    /// between rings triangles
    for (unsigned int i = 0; i < rings - 1; i++ )
    {
        for (unsigned int j = 0; j < segments; j++ )
        {
            indices[index_pos++] = 1 + i * segments + j;
            indices[index_pos++] = 1 + (i + 1) * segments + j;
            indices[index_pos++] = 1 + (i + 1) * segments + ( (j + 1) % segments );

            indices[index_pos++] = 1 + i * segments + j;
            indices[index_pos++] = 1 + (i + 1) * segments + ( (j + 1) % segments );
            indices[index_pos++] = 1 + i * segments + ( (j + 1) % segments ); 
        }
    }

    /// north pole triangles
    for (unsigned int j = 0; j < segments; j++)
    {
        indices[index_pos++] = 1 + (rings - 1) * segments + j;
        indices[index_pos++] = vertex_count - 1;
        indices[index_pos++] = 1 + (rings - 1) * segments + ( (j + 1) % segments ); 
    }

    //ASSERT(index_pos == index_count);

    D3D11_BUFFER_DESC bd;
    ZeroMemory( &bd, sizeof(bd) );
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof( VertexData ) * vertex_count;
    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;
    D3D11_SUBRESOURCE_DATA InitData;
    ZeroMemory( &InitData, sizeof(InitData) );
    InitData.pSysMem = vertices;
    ID3D11Buffer* vertex_buffer;
    HRESULT hr = this->device->CreateBuffer( &bd, &InitData, &vertex_buffer );
    if( FAILED( hr ) )
    {
        throw -1;
    }

    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof( unsigned int ) * index_count;
    bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
    bd.CPUAccessFlags = 0;
    InitData.pSysMem = indices;
    ID3D11Buffer* index_buffer;
    hr = this->device->CreateBuffer( &bd, &InitData, &index_buffer );
    if( FAILED( hr ) )
    {
        throw -1;
    }

    this->sphere_render_object = new RenderObjectDX11(vertex_buffer, sizeof(VertexData), 0, index_buffer, index_count, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    delete indices;
    delete vertices;
}

void RendererDX11::createCubeRenderObject()
{
    // Create vertex buffer
    VertexData vertices[] =
    {
        D3DXVECTOR3( -0.5f, 0.5f, -0.5f ),
        D3DXVECTOR3( -0.5f, 0.5f, 0.5f ),
        D3DXVECTOR3( 0.5f, 0.5f, 0.5f ),
        D3DXVECTOR3( 0.5f, 0.5f, -0.5f ),
        D3DXVECTOR3( -0.5f, -0.5f, -0.5f ),
        D3DXVECTOR3( -0.5f, -0.5f, 0.5f ),
        D3DXVECTOR3( 0.5f, -0.5f, 0.5f ),
        D3DXVECTOR3( 0.5f, -0.5f, -0.5f )
    };
    
    unsigned int indices[] = 
    {
        0, 1, 2,
        0, 2, 3,
        3, 2, 6,
        3, 6, 7,
        0, 3, 7,
        0, 7, 4,
        1, 5, 6,
        1, 6, 2,
        0, 4, 5, 
        0, 5, 1,
        7, 6, 5, 
        7, 5, 4
    };

    D3D11_BUFFER_DESC bd;
    ZeroMemory( &bd, sizeof(bd) );
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof( VertexData ) * 8;
    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;
    D3D11_SUBRESOURCE_DATA InitData;
    ZeroMemory( &InitData, sizeof(InitData) );
    InitData.pSysMem = vertices;
    ID3D11Buffer* vertex_buffer;
    HRESULT hr = this->device->CreateBuffer( &bd, &InitData, &vertex_buffer );
    if( FAILED( hr ) )
    {
        throw -1;
    }

    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof( unsigned int ) * 36;
    bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
    bd.CPUAccessFlags = 0;
    InitData.pSysMem = indices;
    ID3D11Buffer* index_buffer;
    hr = this->device->CreateBuffer( &bd, &InitData, &index_buffer );
    if( FAILED( hr ) )
    {
        throw -1;
    }

    this->cube_render_object = new RenderObjectDX11(vertex_buffer, sizeof(VertexData), 0, index_buffer, 36, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);    
}

void RendererDX11::configViewPoint(Model::Scene* scene)
{
    /// set the viewpoint
    D3DXMATRIX view_matrix;
    D3DXVECTOR3 camera_position;
    D3DXVECTOR3 camera_at;
    D3DXVECTOR3 camera_up;
    if (this->render_3D)
    {
        const Model::Snake* snake = scene->getSnake();
        const Model::Element* head_element = snake->getHeadElement();
        int at_x = 0;
        int at_y = 0;
        snake->getDirection(&at_x, &at_y);
        camera_position = D3DXVECTOR3((float)(head_element->getXPosition() - at_x * 5), (float)(head_element->getYPosition() - at_y * 5), 5.0f);
        camera_at = D3DXVECTOR3((float)(head_element->getXPosition() + at_x), (float)(head_element->getYPosition() + at_y), 0.0f);
        camera_up = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
    }
    else
    {
        camera_position = D3DXVECTOR3((float)scene->getWidth()/2.0f, (float)scene->getHeight() / 2.0f, 20.0f); 
        camera_at = D3DXVECTOR3((float)scene->getWidth()/2.0f, (float)scene->getHeight() / 2.0f, 0.0f); 
        camera_up = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
    }

    D3DXMatrixLookAtLH(&view_matrix, &camera_position, &camera_at, &camera_up);
    this->constant_buffer_data.view = view_matrix;
    
    /// set the projection
    D3DXMATRIX projection_matrix;
    if (this->render_3D)
    {
        D3DXMatrixPerspectiveFovLH(&projection_matrix, 3.14f/2.0f, 1.0f, 0.6f, 1000.0f);
    }
    else
    {
        D3DXMatrixOrthoLH(&projection_matrix, (float)scene->getWidth() + 5.0f, (float)scene->getHeight() + 5.0f, 1.0f, 1000.0f);
    }
    this->constant_buffer_data.projection = projection_matrix;    
}

void RendererDX11::renderSceneWalls(Model::Scene* scene)
{
    RenderObjectDX11* render_object = dynamic_cast<RenderObjectDX11*>(this->cube_render_object);
    // set the vertex buffer
    ID3D11Buffer* vertex_buffer = render_object->getVertexBuffer();
    UINT stride = render_object->getVertexSizeStride();
    UINT offset = render_object->getVertexOffset();
    this->immediate_context->IASetVertexBuffers( 0, 1, &vertex_buffer, &stride, &offset );
    this->immediate_context->IASetInputLayout( this->input_layout );
    
    // Set index buffer
    ID3D11Buffer* index_buffer = render_object->getIndexBuffer();
    this->immediate_context->IASetIndexBuffer( index_buffer, DXGI_FORMAT_R32_UINT, 0 );

    // Set primitive topology
    this->immediate_context->IASetPrimitiveTopology( render_object->getPrimitiveType() );

    // Set the color
    this->constant_buffer_data.color = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f);
 
    /// set the transformation matrix for the walls
    D3DXMATRIX scale_matrix;
    D3DXMATRIX position_matrix;
    D3DXMATRIX world_matrix;
    //vertical walls
    D3DXMatrixScaling(&scale_matrix, 1.0f, (float)scene->getHeight() + 2.0f, 1.0f);
    D3DXMatrixTranslation(&position_matrix, -1.0f, (float)scene->getHeight() / 2.0f - 0.5f, 0.0f);
    D3DXMatrixMultiply(&world_matrix, &scale_matrix, &position_matrix);
    this->constant_buffer_data.world = world_matrix;

    // Set the constant buffer con matrix and color
    this->immediate_context->UpdateSubresource( this->constant_buffer, 0, NULL, &(this->constant_buffer_data), 0, 0 );

    this->immediate_context->VSSetShader( this->vertex_shader, NULL, 0 );
    this->immediate_context->VSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->PSSetShader( this->pixel_shader, NULL, 0 );
    this->immediate_context->PSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->DrawIndexed( render_object->getIndexCount(), 0, 0 );
    

    D3DXMatrixTranslation(&position_matrix, (float)scene->getWidth(), (float)scene->getHeight() / 2.0f - 0.5f, 0.0f);
    D3DXMatrixMultiply(&world_matrix, &scale_matrix, &position_matrix);
    this->constant_buffer_data.world = world_matrix;

    // Set the constant buffer con matrix and color
    this->immediate_context->UpdateSubresource( this->constant_buffer, 0, NULL, &(this->constant_buffer_data), 0, 0 );

    //
    // Render the cube
    //
    this->immediate_context->VSSetShader( this->vertex_shader, NULL, 0 );
    this->immediate_context->VSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->PSSetShader( this->pixel_shader, NULL, 0 );
    this->immediate_context->PSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->DrawIndexed( render_object->getIndexCount(), 0, 0 );

    //horizontal walls
    D3DXMatrixScaling(&scale_matrix, (float)scene->getWidth()  + 2.0f, 1.0f, 1.0f);
    D3DXMatrixTranslation(&position_matrix, (float)scene->getWidth() / 2.0f - 0.5f, -1.0f, 0.0f);
    D3DXMatrixMultiply(&world_matrix, &scale_matrix, &position_matrix);
    this->constant_buffer_data.world = world_matrix;

    // Set the constant buffer con matrix and color
    this->immediate_context->UpdateSubresource( this->constant_buffer, 0, NULL, &(this->constant_buffer_data), 0, 0 );

    //
    // Render the cube
    //
    this->immediate_context->VSSetShader( this->vertex_shader, NULL, 0 );
    this->immediate_context->VSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->PSSetShader( this->pixel_shader, NULL, 0 );
    this->immediate_context->PSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->DrawIndexed( render_object->getIndexCount(), 0, 0 );

    D3DXMatrixTranslation(&position_matrix, (float)scene->getWidth() / 2.0f - 0.5f, (float)scene->getHeight(), 0.0f);
    D3DXMatrixMultiply(&world_matrix, &scale_matrix, &position_matrix);
    this->constant_buffer_data.world = world_matrix;

    // Set the constant buffer con matrix and color
    this->immediate_context->UpdateSubresource( this->constant_buffer, 0, NULL, &(this->constant_buffer_data), 0, 0 );

    //
    // Render the cube
    //
    this->immediate_context->VSSetShader( this->vertex_shader, NULL, 0 );
    this->immediate_context->VSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->PSSetShader( this->pixel_shader, NULL, 0 );
    this->immediate_context->PSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->DrawIndexed( render_object->getIndexCount(), 0, 0 );
}

void RendererDX11::renderSnakeHead(const Model::Element* &element)
{
    RenderObjectDX11* render_object = dynamic_cast<RenderObjectDX11*>(this->sphere_render_object);
    // set the vertex buffer
    ID3D11Buffer* vertex_buffer = render_object->getVertexBuffer();
    UINT stride = render_object->getVertexSizeStride();
    UINT offset = render_object->getVertexOffset();
    this->immediate_context->IASetVertexBuffers( 0, 1, &vertex_buffer, &stride, &offset );
    this->immediate_context->IASetInputLayout( this->input_layout );

    // Set index buffer
    ID3D11Buffer* index_buffer = render_object->getIndexBuffer();
    this->immediate_context->IASetIndexBuffer( index_buffer, DXGI_FORMAT_R32_UINT, 0 );

    // Set primitive topology
    this->immediate_context->IASetPrimitiveTopology( render_object->getPrimitiveType() );

    // Set the color
    this->constant_buffer_data.color = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 1.0f);

    
    /// set the transformation matrix for the element
    D3DXMATRIX scale_matrix;
    D3DXMatrixScaling(&scale_matrix, 0.45f, 0.45f, 0.45f);
    
    D3DXMATRIX position_matrix;
    D3DXMatrixTranslation(&position_matrix, element->getXPosition(), element->getYPosition(), 0.0f);

    D3DXMATRIX world_matrix;
    D3DXMatrixMultiply(&world_matrix, &scale_matrix, &position_matrix);

    this->constant_buffer_data.world = world_matrix;

    // Set the constant buffer with matrix and color
    this->immediate_context->UpdateSubresource( this->constant_buffer, 0, NULL, &(this->constant_buffer_data), 0, 0 );

    this->immediate_context->VSSetShader( this->vertex_shader, NULL, 0 );
    this->immediate_context->VSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->PSSetShader( this->pixel_shader, NULL, 0 );
    this->immediate_context->PSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->DrawIndexed( render_object->getIndexCount(), 0, 0 );
}

void RendererDX11::renderSnakeBodyElement(const Model::Element* &element)
{
    RenderObjectDX11* render_object = dynamic_cast<RenderObjectDX11*>(this->cube_render_object);
    // set the vertex buffer
    ID3D11Buffer* vertex_buffer = render_object->getVertexBuffer();
    UINT stride = render_object->getVertexSizeStride();
    UINT offset = render_object->getVertexOffset();
    this->immediate_context->IASetVertexBuffers( 0, 1, &vertex_buffer, &stride, &offset );
    this->immediate_context->IASetInputLayout( this->input_layout );

    // Set index buffer
    ID3D11Buffer* index_buffer = render_object->getIndexBuffer();
    this->immediate_context->IASetIndexBuffer( index_buffer, DXGI_FORMAT_R32_UINT, 0 );

    // Set primitive topology
    this->immediate_context->IASetPrimitiveTopology( render_object->getPrimitiveType() );

    // Set the color
    this->constant_buffer_data.color = D3DXVECTOR4(0.0f, 0.0f, 1.0f, 1.0f);

    /// set the transformation matrix for the element
    D3DXMATRIX scale_matrix;
    D3DXMatrixScaling(&scale_matrix, 0.9f, 0.9f, 0.9f);

    D3DXMATRIX position_matrix;
    D3DXMatrixTranslation(&position_matrix, element->getXPosition(), element->getYPosition(), 0.0f);

    D3DXMATRIX world_matrix;
    D3DXMatrixMultiply(&world_matrix, &scale_matrix, &position_matrix);
    this->constant_buffer_data.world = world_matrix;

    // Set the constant buffer with matrix and color
    this->immediate_context->UpdateSubresource( this->constant_buffer, 0, NULL, &(this->constant_buffer_data), 0, 0 );

    this->immediate_context->VSSetShader( this->vertex_shader, NULL, 0 );
    this->immediate_context->VSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->PSSetShader( this->pixel_shader, NULL, 0 );
    this->immediate_context->PSSetConstantBuffers( 0, 1, &this->constant_buffer );
    this->immediate_context->DrawIndexed( render_object->getIndexCount(), 0, 0 );
}

void RendererDX11::drawText()
{
    // There is not support in DX11 to render Text,
    // It should be done with DirectWrite.
    // It is a wrapper to use, bu it will need extra work.
    // http://fw1.codeplex.com/
    // Another useful link is: http://www.rastertek.com/dx11tut12.html
}

bool RendererDX11::compileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut )
{
    bool result_ok = true;

    DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif

    ID3DBlob* pErrorBlob;
    HRESULT hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel, 
        dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
    if( FAILED(hr) )
    {
        if( pErrorBlob != NULL )
            OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
        if( pErrorBlob ) pErrorBlob->Release();
        return false;
    }
    if( pErrorBlob ) pErrorBlob->Release();

    return true;
}
